<?php
/**
 * Created by PhpStorm.
 * User: lhl
 * Date: 2020/12/23
 * Time: 13:17
 * Features: 资产组管理
 */

namespace app\adminapiv3\model;


use app\common\service\StringHelper;
use think\Db;
use think\Exception;

class ServerHostGroupModel extends BaseModel
{
    /**
     * 搜索器
     * @field name 资产组名称
     */
    public function searchNameAttr($query, $value, $data){
        $value = trim($value);
        if(strlen($value)){
            $query->where('name','like',"%{$value}%");
        }
    }

    /**
     * 搜索器
     * @field description 资产组描述
     */
    public function searchDescriptionAttr($query, $value, $data){
        $value = trim($value);
        if(strlen($value)){
            $query->where('description','like',"%{$value}%");
        }
    }


    /**
     * 搜索器
     * @field check_code 主机绑定码
     */
    public function searchCheckCodeAttr($query, $value, $data){
        $value = trim($value);
        if(strlen($value)){
            $query->where('check_code','like',"%{$value}%");
        }
    }

    /**
     * 搜索器
     * @field create_time 创建时间
     */
    public function searchCreateTimeAttr($query, $value, $data){
        $value = trim($value);
        if(strlen($value)){
            $startTime = strtotime($value);
            $endTime = $startTime + 86399;
            $query->whereBetweenTime('create_time',$startTime,$endTime);
        }
    }

    /**
     * 查询资产组列表
     * @param array $search 搜索参数
     */
    public function getList($search = [])
    {
        return $this->withSearch(['name','description','check_code','create_time'],$search)->paginate()->toArray();
    }

    /**
     * 查询所有资产组
     * @param string $field 查询字段
    */
    public function getAllList($field = '*'){
        return $this->field($field)->select()->toArray();
    }

    /**
     * 验证资产组id是否存在
     * @param int $hostGroupId 资产组id
     *
     * @return mixed
    */
    public function checkHostGroupExists($hostGroupId){
        $value = $this->where('id',$hostGroupId)->field('id,name')->findOrEmpty();
        return $value ? $value :false;
    }

    /**
     * 查询回显所需数据
     * @param array  $param 表单数据
     * @param string $scene 场景 ： add - 添加 modify - 修改
     */
    public function getInfo($param = [],$scene = 'add'){
        //查询所有的模块授权信息
        $authModuleModel = new ServerAuthModuleModel();
        $limits = $authModuleModel->getAllAuthModule('id as module_id,module_key,module_name,number,etime');

        //所有模块信息
        $data['modules'] = [];
        //模块参数的界限值信息
        $data['limit'] = [];

        //查询回显 已勾选的模块授权数据
        if($scene == 'modify'){
            //该资产组已勾选授权的信息
            $data['info'] = model('ServerHostGroupModule')->getGroupAuthInfo($param['id'],'module_id,number,etime');
        }

        foreach ($limits as $key => $module){
            //所有模块信息
            $data['modules'][$key] = [
                'module_id' => $module['module_id'],
                'module_name' => $module['module_name'],
                'is_show' => true, //展示状态
            ];

            //界限值信息 默认值
            $data['limit'][$key] = [
                'module_id' => $module['module_id'], //模块id
            ];

            if($scene == 'modify'){
                $exceptId = $param['id'];
                //查询主机数量最小值
                $min = model('HostModuleRelation')->getMinAuthNum($param['id'],$module['module_key']);
                //条件2 ： 每个模块的基础最小值为1
                $data['limit'][$key]['number_min'] = max($min,1);
            }else{
                $exceptId = 0;
                //添加时 ：最小值为1
                $data['limit'][$key]['number_min'] = 1;
            }

            //该模块的已经授权数
            $existNum = model('ServerHostGroupModule')->getTotalAuthNum($module['module_id'],$exceptId);

            //最大值 max 这里要改成这个模块的所有主机之和 server_host_group_module 表 根据 module_id 对number字段求sum
            $max = $module['number'] - $existNum > 0 ? $module['number'] - $existNum : 0;
            $data['limit'][$key]['number_max'] = $max;

            //结束时间 0 代表永久授权 则不限制
            $data['limit'][$key]['etime_max'] = $module['etime'];

            //添加时,如果主机数量最小值与最大值区间错位，则不展示该模块
            if($scene != 'modify' && $data['limit'][$key]['number_min'] > $data['limit'][$key]['number_max']){
                $data['modules'][$key]['is_show'] = false; //展示状态
            }
        }

        return $data;
    }


    /**
     * 对主机数量和授权结束时间进行验证
     * @param array     $param 表单数据
     * @param string    $scene 场景:add 添加 modify 修改
     *
     * @return bool | array
     */
    public function checkLimit(array $param,$scene = 'add'){
        //查询所有的模块授权信息 (界限值)
        $authModuleModel = new ServerAuthModuleModel();
        $limit = $authModuleModel->getAllAuthModule('id as module_id,module_key,module_name,number,etime');
        if(!$limit){
            $this->setError($this->CODE_FAIL,'缺少授权数据,请先授权');
            return false;
        }


        //验证授权参数
        //1.模块id必须存在
        $limitModuleIds = array_column($limit,'module_id');
        $inputModuleIds = array_map('intval',array_column($param['module'],'module_id'));

        if(array_merge(array_intersect($limitModuleIds,$inputModuleIds)) != array_merge($inputModuleIds)){
            $this->setError($this->CODE_FAIL,'无效的模块');
            return false;
        }

        //日志数据
        $log = [];

        //对授权数量和授权结束时间进行验证
        foreach ($param['module'] as $key => $module){
            $curModuleLimit = [];
            //匹配当前模块
            foreach ($limit as $k => $v){
                if($module['module_id'] == $v['module_id']){
                    $curModuleLimit = $v;
                    break;
                }
            }

            //返回日志数据
            $log[] = [
                'module_name' => $curModuleLimit['module_name'],
                'number' => $module['number'],
                'etime' => date('Y-m-d',strtotime($module['etime']))
            ];

            //修改时
            if($scene == 'modify'){
                $exceptId = $param['id'];
                //查询主机数量最小值
                $min = model('HostModuleRelation')->getMinAuthNum($param['id'],$curModuleLimit['module_key']);
                if($min >= 1){
                    $msg = '已绑定主机数';
                }else{
                    $msg = '';
                }

                //条件2 ： 每个模块的基础最小值为1
                $min = max($min,1);
                if($module['number'] < $min){
                    $this->setError($this->CODE_FAIL,"{$curModuleLimit['module_name']}模块的主机数量不能低于{$msg}:{$min}" );
                    return false;
                }

            }else{
                //添加时
                $exceptId = 0;
                //添加时 ：最小值为1
                if($module['number'] < 1){
                    $this->setError($this->CODE_FAIL,"{$curModuleLimit['module_name']}模块的主机数量不能低于1" );
                    return false;
                }
            }

            //该模块的已经授权数
            $existNum = model('ServerHostGroupModule')->getTotalAuthNum($module['module_id'],$exceptId);

            //主机数量不能大于系统的最大授权数量
            if($module['number'] + $existNum > $curModuleLimit['number']){
                $remainNum = $curModuleLimit['number'] - $existNum > 0 ? $curModuleLimit['number'] - $existNum : 0;
                $this->setError($this->CODE_FAIL,"{$curModuleLimit['module_name']}模块的主机数量不能超过:$remainNum" );
                return false;
            }

            //验证结束时间 ： 填入的结束时间 不能 大于限定值的时间 (永久授权除外)
            $module['etime'] = strtotime($module['etime']);
            if($curModuleLimit['etime'] && $module['etime'] > $curModuleLimit['etime']){
                $this->setError($this->CODE_FAIL,"{$curModuleLimit['module_name']}模块的授权时间不能超过:".date('Y-m-d',$curModuleLimit['etime']));
                return false;
            }

        }
        return $log;
    }

    /**
     * 添加资产组
     * @param array $param 表单数据
     */
    public function addGroup($param){
        //记录操作日志
        global $logArray;
        $logArray = array(
            'content' => '添加资产组',
        );
        //对主机数量和授权结束时间进行验证
        $res = $this->checkLimit($param,'add');
        if($res === false){
            return false;
        }

        //补充参数
        $param = array_merge($param,[
            'check_code' => StringHelper::uniqueMd5(),
            'create_time' => time()
        ]);

        $inputModule = $param['module'];
        unset($param['module']);

        //保存数据
        $this->startTrans();
        try{
            //1.写入资产组表
            $id = $this->insertGetId($param);
            if(!$id){
                $this->rollback();
                $this->setError($this->CODE_FAIL,'操作失败');
                return false;
            }

            //2.写入资产组模块授权表
            foreach ($inputModule as $key => $module){
                $inputModule[$key]['group_id'] = $id;
                $inputModule[$key]['etime'] = strtotime($module['etime']);
            }


            //统一先删除后添加
            model('ServerHostGroupModule')->where('group_id',$id)->delete();

            if (!model('ServerHostGroupModule')->insertAll($inputModule)) {
                $this->rollback();
                $this->setError($this->CODE_FAIL,'操作失败');
                return false;
            }

            $this->commit();
        }catch (Exception $e){
            $this->rollback();
            $this->setError($this->CODE_FAIL,'操作失败');
            return false;
        }

        //记录操作日志
        $logArray['new_info'] = "资产组名称：{$param['name']}；资产组描述：{$param['description']}；".PHP_EOL;
        //模块授权信息
        foreach ($res as $log){
            $logArray['new_info'] .= "模块名称：{$log['module_name']}；授权数量：{$log['number']}；授权时间：{$log['etime']}" . PHP_EOL;
        }

        return true;
    }

    /**
     * 修改资产组
     * @param array $param 表单数据
     */
    public function modifyGroup($param){
        //记录操作日志
        global $logArray;
        $logArray = array(
            'content' => '修改资产组',
        );

        //对主机数量和授权结束时间进行验证
        $res = $this->checkLimit($param,'modify');
        if($res === false){
            return false;
        }
        //查询信息
        $info = $this->where('id',$param['id'])->findOrEmpty()->toArray();
        if(!$info){
            $this->setError($this->CODE_FAIL,'资产组不存在');
            return false;
        }

        //查询原来的模块授权信息
        $oldLog = model('ServerHostGroupModule')->getGroupAuthDetail($param['id'],'m.module_name,g.number,g.etime');

        $param = array_merge($param,[
            'update_time' => time()
        ]);

        $inputModule = $param['module'];
        unset($param['module']);

        //保存数据
        $this->startTrans();
        try{
            //1.保存资产组表
            $this->update($param,['id' => $param['id']]);

            //2.写入资产组模块授权表
            foreach ($inputModule as $key => $module){
                $inputModule[$key]['group_id'] = $param['id'];
                $inputModule[$key]['etime'] = strtotime($module['etime']);
            }

            //统一先删除后添加
            model('ServerHostGroupModule')->where('group_id',$param['id'])->delete();
            if (!model('ServerHostGroupModule')->insertAll($inputModule)) {
                $this->rollback();
                $this->setError($this->CODE_FAIL,'操作失败');
                return false;
            }

            $this->commit();
        }catch (Exception $e){
            $this->rollback();
            $this->setError($this->CODE_FAIL,'操作失败');
            return false;
        }

        //记录操作日志
        $logArray['new_info'] = "资产组名称：{$param['name']}；资产组描述：{$param['description']}；".PHP_EOL;
        //模块授权信息
        foreach ($res as $log){
            $logArray['new_info'] .= "模块名称：{$log['module_name']}；授权数量：{$log['number']}；授权时间：{$log['etime']}" . PHP_EOL;
        }

        $logArray['old_info'] = "资产组名称：{$info['name']}；资产组描述：{$info['description']}；";
        //模块授权信息
        foreach ($oldLog as $log){
            $log['etime'] = date('Y-m-d',$log['etime']);
            $logArray['old_info'] .= "模块名称：{$log['module_name']}；授权数量：{$log['number']}；授权时间：{$log['etime']}" . PHP_EOL;
        }

        return true;
    }

    /**
     * 批量删除资产组
     * @param string $ids 资产组id逗号隔开字符串
     */
    public function delGroup($ids){
        //记录操作日志
        global $logArray;
        $logArray = array(
            'content' => '删除资产组',
        );

        //查询资产组信息
        $list = $this->where('id','in',$ids)->select()->toArray();
        if(!$list){
            $this->setError($this->CODE_FAIL,'数据不存在');
            return false;
        }

        $ids = array_column($list,'id');

        //资产组下有用户,则不能删除
        $userModel = new ServerUserModel();
        $users = $userModel->getUsersByHostGroupId($ids,'id');
        if($users){
            $this->setError($this->CODE_FAIL,'所选资产组下存在用户,无法删除');
            return false;
        }

        //资产组下有主机,则不能删除
        if (Db::name('host')->where('group_id','in',$ids)->value('id')) {
            $this->setError($this->CODE_FAIL,'所选资产组已经绑定了主机,无法删除');
            return false;
        }

        //本次删除的资产组数量
        $count = count($ids);

        //保存数据
        $this->startTrans();
        try{
            //删除资产组数据
            if (!$this->where('id','in',$ids)->delete()) {
                $this->rollback();
                $this->setError($this->CODE_FAIL,'删除失败');
                return false;
            }

            //删除资产组模块授权信息表数据
            Db::name('server_host_group_module')->where('group_id','in',$ids)->delete();

            $this->commit();

            //记录日志
            $logArray['old_info'] = "一共删除{$count}个资产组；资产组名称为".implode(',',array_column($list,'name'));
            return true;
        }catch (Exception $e){
            $this->rollback();
            $this->setError($this->CODE_FAIL,'删除失败');
            return false;
        }
    }

}